/**
 * Copyright (C) 2009+ Spark Zheng
 *
 * @file        src/filter.c 
 * @date        2010-04-05
 * @brief	general_datastream
 * @see         dserver
 * @history
 */

#include <dlib/str.h>
#include <dlib/mempool.h>
#include <dlib/dlloader.h>

#include "dserver_log.h"
#include "dserver_errno.h"
#include "filter.h"

static inline void _filter_settings(dserver_setting_t *s, filter_node_t *filter);
static inline void _filter_unsettings(filter_node_t *filter);

static inline int _filter_load(dserver_setting_t *s, filter_node_t *filter);
static inline void _filter_unload(filter_node_t *filter);

static inline int _filter_load_all(dserver_t *dc, dserver_filter_t *filters);
static inline void _filter_unload_all(dserver_filter_t *filters);

static inline void _filter_settings(dserver_setting_t *s, filter_node_t *filter)
{
	int ret;
	long integer;
	dlib_confl_t *confl = s->dserver_confl;

	ret = dlib_confl_get_integer(confl, filter->f_name, F_THREAD_MIN, &integer);
	if (ret == -E_CONFL_FIELD_NOTEXIST || ret == -E_CONFL_ITEM_NOTEXIST) {
		filter->f_settings.f_pmin = F_DEF_TMIN;
	} else if (ret == 0) {
		filter->f_settings.f_pmin = (uint16_t)integer;
	}
	if (filter->f_settings.f_pmin == 0) {
		filter->f_settings.f_pmin = F_DEF_TMIN;
	}

	ret = dlib_confl_get_integer(confl, filter->f_name, F_THREAD_MAX, &integer);
	if (ret == -E_CONFL_FIELD_NOTEXIST || ret == -E_CONFL_ITEM_NOTEXIST) {
		filter->f_settings.f_pmax = F_DEF_TMAX;
	} else if (ret == 0) {
		filter->f_settings.f_pmax = (uint16_t)integer;
	}
	if (filter->f_settings.f_pmax == 0) {
		filter->f_settings.f_pmax = F_DEF_TMAX;
	}

	ret = dlib_confl_get_integer(confl, filter->f_name, F_TIMEOUT, &integer);
	if (ret == -E_CONFL_FIELD_NOTEXIST || ret == -E_CONFL_ITEM_NOTEXIST) {
		filter->f_settings.f_timeout = F_DEF_TIMEOUT;
	} else if (ret == 0) {
		filter->f_settings.f_timeout = (uint16_t)integer;
	}
	if (filter->f_settings.f_timeout == 0) {
		filter->f_settings.f_timeout = F_DEF_TIMEOUT;
	}

	ret = dlib_confl_get_integer(confl, filter->f_name, F_PRIORITY, &integer);
	if (ret == -E_CONFL_FIELD_NOTEXIST || ret == -E_CONFL_ITEM_NOTEXIST) {
		filter->f_settings.f_priority = F_DEF_PRIORITY;
	} else if (ret == 0) {
		filter->f_settings.f_priority = (uint16_t)integer;
	}
	if (filter->f_settings.f_priority == 0) {
		filter->f_settings.f_priority = F_DEF_PRIORITY;
	}

	ret = dlib_confl_get_integer(confl, filter->f_name, F_PIPELINE_ID, &integer);
	if (ret == -E_CONFL_FIELD_NOTEXIST || ret == -E_CONFL_ITEM_NOTEXIST) {
		filter->f_settings.f_pipeline_id = F_DEF_PIPELINE_ID;
	} else if (ret == 0) {
		filter->f_settings.f_pipeline_id = (uint16_t)integer;
	}
	if (filter->f_settings.f_pipeline_id == 0) {
		filter->f_settings.f_pipeline_id = F_DEF_PIPELINE_ID;
	}

	ret = dlib_confl_get_integer(confl, filter->f_name, F_FILTER_ID, &integer);
	if (ret == -E_CONFL_FIELD_NOTEXIST || ret == -E_CONFL_ITEM_NOTEXIST) {
		filter->f_settings.f_filter_id = F_DEF_FILTER_ID;
	} else if (ret == 0) {
		filter->f_settings.f_filter_id = (uint16_t)integer;
	}
	if (filter->f_settings.f_filter_id == 0) {
		filter->f_settings.f_filter_id = F_DEF_FILTER_ID;
	}

	return;
}

static inline void _filter_unsettings(filter_node_t *filter)
{
	UNUSED_PARAM(filter);
	return;
}

static inline int _filter_load(dserver_setting_t *s, filter_node_t *filter)
{
	int ret = 0;
	char lib_file[PATH_MAX] = {0};
	void *handler = NULL;
	
	/* library-file parser */
	if (s->filters_dir != NULL) {
		snprintf(lib_file, PATH_MAX, "%s/lib%s.so", s->filters_dir, filter->f_name);
	} else {
		snprintf(lib_file, PATH_MAX, "lib%s.so", filter->f_name);
	}
	
	/* read the ops of the filter */
	handler = dlopen(lib_file, RTLD_LAZY);
	filter->f_handler = handler;
	if (handler == NULL) {
		DLOG_ERR(LOG_MAIN, "dlopen %s error: %s", lib_file, dlerror());
		return -E_FILE_OPEN;
	}
	
	filter->f_ops.f_filter_load = dlsym(handler, FILTER_LOAD_STR);
	filter->f_ops.f_stream_init = dlsym(handler, STREAM_INIT_STR);
	filter->f_ops.f_stream_worker = dlsym(handler, STREAM_WORKER_STR);
	filter->f_ops.f_stream_timeout = dlsym(handler, STREAM_TIMEOUT_STR);
	filter->f_ops.f_stream_finish = dlsym(handler, STREAM_FINISH_STR);
	filter->f_ops.f_stream_exit = dlsym(handler, STREAM_EXIT_STR);
	filter->f_ops.f_filter_unload = dlsym(handler, FILTER_UNLOAD_STR);

	/* init_filter */
	if (filter->f_ops.f_filter_load != NULL) {
		ret = (*filter->f_ops.f_filter_load)(s, filter);
		if (ret < 0) {
			DLOG_ERR(LOG_MAIN, "filter %s init error.", filter->f_name);
			return ret;
		}
	} else {
		DLOG_DEBUG(LOG_MAIN, "filter %s without init function.", filter->f_name);	
	}
	
	DLOG_INFO(LOG_MAIN, "[FILTER]load_filter %s successfully.", filter->f_name);

	return RET_SUCCESS;
}

static inline void _filter_unload(filter_node_t *filter)
{
	if (filter == NULL) {
		return;
	}
	if (filter->f_ops.f_filter_unload != NULL) {
		(*filter->f_ops.f_filter_unload)(filter);
	} else {
		DLOG_DEBUG(LOG_MAIN, "filter %s without exit function.", filter->f_name);
	}
	if (filter->f_handler != NULL) {
		dlclose(filter->f_handler);
	}
	return;
}

static inline int _filter_load_all(dserver_t *dc, dserver_filter_t *filters)
{
	int ret = 0;
	int len = 0;
	char *filter_begin = NULL;
	char *filter_end = NULL;
	
	filter_node_t *filter = NULL;
	dserver_setting_t *s = dc->settings;
	
	if (s->filters == NULL || s->dserver_confl == NULL)
		return -E_ARGS_ILLEGAL;
	
	filter_begin = s->filters;
	do {
		filter = dmalloc(sizeof(*filter));
		
		filter_end = strchr(filter_begin, ',');
		if (filter_end == NULL) {
			len = strlen(filter_begin);
			filter->f_name = dmalloc(len + 1);
			memcpy(filter->f_name, filter_begin, len);
			str_trim(filter->f_name);
			filter_begin = NULL;
		} else {
			len = filter_end - filter_begin;
			filter->f_name = dmalloc(len + 1);
			memcpy(filter->f_name, filter_begin, len);
			str_trim(filter->f_name);
			filter_begin = filter_end + 1;
		}

		/* initialize filter_settings, it's needed by pipeline */
		_filter_settings(s, filter);
		
		/* initialize filter_ops and do filter_init */
		ret = _filter_load(s, filter);
		if (ret < 0) {
			DLOG_WARNING(LOG_MAIN, "load filter %s error[%d], discard.",
					filter->f_name, ret);
			_filter_unsettings(filter);
			dfree(filter->f_name);
			dfree(filter);
			continue;
		} else {
			//dlib_lock_lock(&filters->f_lock);
			dlib_atomic_inc(&filters->f_nr);
			LIST_ADD_TAIL(&filters->f_head, &filter->f_node);
			//dlib_lock_unlock(&filters->f_lock);
		}
	} while (filter_begin != NULL);
	
	return RET_SUCCESS;
}

static inline void _filter_unload_all(dserver_filter_t *filters)
{
	filter_node_t *tpos = NULL;
	list_node_t *pos = NULL;
	list_node_t *n = NULL;
	
	if (filters == NULL || dlib_atomic_test(&filters->f_nr, 0)) {
		return;
	}

	//dlib_lock_lock(&filters->f_lock);
	LIST_FOREACH_ENTRY_SAFE(tpos, pos, n, &filters->f_head, f_node) {
		dlib_atomic_dec(&filters->f_nr);
		LIST_DEL(&tpos->f_node);

		_filter_unload(tpos);
		_filter_unsettings(tpos);
		dfree(tpos->f_name);
		dfree(tpos);
	}
	//dlib_lock_unlock(&filters->f_lock);
	
	return;
}

int filter_init(dserver_t *dc)
{
	int ret = RET_SUCCESS;
	dserver_filter_t *filters;
	
	DLOG_DEBUG(LOG_MAIN, "dserver enters in filter_init function.");
	if (dc == NULL) {
		DLOG_ERR(LOG_MAIN, "filter_init parameters error.");
		return -E_ARGS_ILLEGAL;
	}

	filters = dmalloc(sizeof(*filters));
	dc->filters = filters;
	
	//dlib_lock_init(&filters->f_lock);
	dlib_atomic_init(&filters->f_nr, 0);
	LIST_HEAD_INIT(&filters->f_head);

	ret = _filter_load_all(dc, filters);
	if (ret < 0) {
		DLOG_ERR(LOG_MAIN, "filter_load error, unexpected.");
		return ret;
	}

	return ret;
}

void filter_exit(dserver_t *dc)
{
	dserver_filter_t *filters;
	
	DLOG_DEBUG(LOG_MAIN, "dserver enters in filter_exit function.");
	if (dc == NULL || dc->filters == NULL) {
		return;
	}

	filters = dc->filters;
	_filter_unload_all(filters);
	//dlib_lock_exit(&filters->f_lock);
	dfree(filters);

	return;
}
